#pragma once
#include <Rz/Config.h>

namespace Rz {

#define RZ_DEFINE_ZERO_ENUMFLAG		0
#define RZ_DEFINE_ENUMFLAG(n)		(1 << n)
#define _RZ_ENUMMANAGER_DEFINE_METHOD(type)		static RZ_INLINE type PromoteEnum(type e) { return e; }

struct EnumManager
{
	_RZ_ENUMMANAGER_DEFINE_METHOD(u8);
	_RZ_ENUMMANAGER_DEFINE_METHOD(u16);
	_RZ_ENUMMANAGER_DEFINE_METHOD(u32);
	_RZ_ENUMMANAGER_DEFINE_METHOD(u64);
	_RZ_ENUMMANAGER_DEFINE_METHOD(s8);
	_RZ_ENUMMANAGER_DEFINE_METHOD(s16);
	_RZ_ENUMMANAGER_DEFINE_METHOD(s32);
	_RZ_ENUMMANAGER_DEFINE_METHOD(s64);
};

#define RZ_MARK_AS_TYPED_ENUMFLAGS(EnumType, type)\
RZ_INLINE bool     operator !  (EnumType  lhs)				 { return bool( ! type (lhs) ); }\
RZ_INLINE EnumType operator ~  (EnumType  lhs)				 { return EnumType( ~ type (lhs) ); }\
RZ_INLINE EnumType operator |  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) | type (rhs) ); }\
RZ_INLINE EnumType operator &  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) & type (rhs) ); }\
RZ_INLINE EnumType operator ^  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) ^ type (rhs) ); }\
RZ_INLINE EnumType operator |= (EnumType& lhs, EnumType rhs) { return lhs = lhs | rhs; }\
RZ_INLINE EnumType operator &= (EnumType& lhs, EnumType rhs) { return lhs = lhs & rhs; }\
RZ_INLINE EnumType operator ^= (EnumType& lhs, EnumType rhs) { return lhs = lhs ^ rhs; }

#define RZ_MARK_AS_TYPED_ENUMFLAGS_FRIEND(EnumType, type)\
RZ_INLINE friend bool     operator !  (EnumType  lhs)				{ return bool( ! type (lhs) ); }\
RZ_INLINE friend EnumType operator ~  (EnumType  lhs)				{ return EnumType( ~ type (lhs) ); }\
RZ_INLINE friend EnumType operator |  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) | type (rhs) ); }\
RZ_INLINE friend EnumType operator &  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) & type (rhs) ); }\
RZ_INLINE friend EnumType operator ^  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) ^ type (rhs) ); }\
RZ_INLINE friend EnumType operator |= (EnumType& lhs, EnumType rhs) { return lhs = lhs | rhs; }\
RZ_INLINE friend EnumType operator &= (EnumType& lhs, EnumType rhs) { return lhs = lhs & rhs; }\
RZ_INLINE friend EnumType operator ^= (EnumType& lhs, EnumType rhs) { return lhs = lhs ^ rhs; }

#define RZ_TYPED_ENUMFLAGS_GENERATE_ARITHMETICS(EnumType, type)\
RZ_INLINE EnumType operator ++ (EnumType&  lhs)				 { ++(type&)lhs; return lhs; }\
RZ_INLINE EnumType operator ++ (EnumType&  lhs, int)		 { EnumType t = lhs; ++lhs; return t; }\
RZ_INLINE EnumType operator -- (EnumType&  lhs)				 { ++(type&)lhs; return lhs; }\
RZ_INLINE EnumType operator -- (EnumType&  lhs, int)		 { EnumType t = lhs; --lhs; return t; }\
RZ_INLINE EnumType operator +  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) + type (rhs) ); }\
RZ_INLINE EnumType operator -  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) - type (rhs) ); }\
RZ_INLINE EnumType operator *  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) * type (rhs) ); }\
RZ_INLINE EnumType operator /  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) / type (rhs) ); }\
RZ_INLINE EnumType operator += (EnumType& lhs, EnumType rhs) { return lhs = lhs + rhs; }\
RZ_INLINE EnumType operator -= (EnumType& lhs, EnumType rhs) { return lhs = lhs - rhs; }\
RZ_INLINE EnumType operator *= (EnumType& lhs, EnumType rhs) { return lhs = lhs * rhs; }\
RZ_INLINE EnumType operator /= (EnumType& lhs, EnumType rhs) { return lhs = lhs / rhs; }

#define RZ_TYPED_ENUMFLAGS_GENERATE_ARITHMETICS_FRIEND(EnumType, type)\
RZ_INLINE friend EnumType operator ++ (EnumType&  lhs)				{ ++*(type*)lhs; return lhs; }\
RZ_INLINE friend EnumType operator ++ (EnumType&  lhs, int)			{ EnumType t = lhs; ++lhs; return t; }\
RZ_INLINE friend EnumType operator -- (EnumType&  lhs)				{ ++*(type*)lhs; return lhs; }\
RZ_INLINE friend EnumType operator -- (EnumType&  lhs, int)			{ EnumType t = lhs; --lhs; return t; }\
RZ_INLINE friend EnumType operator +  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) + type (rhs) ); }\
RZ_INLINE friend EnumType operator -  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) - type (rhs) ); }\
RZ_INLINE friend EnumType operator *  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) * type (rhs) ); }\
RZ_INLINE friend EnumType operator /  (EnumType  lhs, EnumType rhs) { return EnumType( type (lhs) / type (rhs) ); }\
RZ_INLINE friend EnumType operator += (EnumType& lhs, EnumType rhs) { return lhs = lhs + rhs; }\
RZ_INLINE friend EnumType operator -= (EnumType& lhs, EnumType rhs) { return lhs = lhs - rhs; }\
RZ_INLINE friend EnumType operator *= (EnumType& lhs, EnumType rhs) { return lhs = lhs * rhs; }\
RZ_INLINE friend EnumType operator /= (EnumType& lhs, EnumType rhs) { return lhs = lhs / rhs; }

#define RZ_MARK_AS_ENUMFLAGS(EnumType)			RZ_MARK_AS_TYPED_ENUMFLAGS(EnumType, int)
#define RZ_MARK_AS_ENUMFLAGS_FRIEND(EnumType)	RZ_MARK_AS_TYPED_ENUMFLAGS_FRIEND(EnumType, int)

#define RZ_ENUMFLAGS_GENERATE_ARITHMETICS(EnumType)			RZ_TYPED_ENUMFLAGS_GENERATE_ARITHMETICS(EnumType, int)
#define RZ_ENUMFLAGS_GENERATE_ARITHMETICS_FRIEND(EnumType)	RZ_TYPED_ENUMFLAGS_GENERATE_ARITHMETICS_FRIEND(EnumType, int)

}